home *** CD-ROM | disk | FTP | other *** search
- /*
- * linux/kernel/signal.c
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file README.legal in the main directory of this archive
- * for more details.
- */
-
- /*
- * 680x0 support by Hamish Macdonald
- */
-
- #include <asm/system.h>
- #include <asm/segment.h>
-
- #include <linux/sched.h>
- #include <linux/kernel.h>
- #include <linux/traps.h>
- #include <linux/signal.h>
- #include <linux/errno.h>
- #include <linux/wait.h>
- #include <linux/ptrace.h>
- #include <linux/unistd.h>
-
- #define _S(nr) (1<<((nr)-1))
-
- #define _BLOCKABLE (~(_S(SIGKILL) | _S(SIGSTOP)))
-
- asmlinkage int do_signal(unsigned long oldmask, struct frame * regs);
-
- asmlinkage int sys_sigprocmask(int how, sigset_t *set, sigset_t *oset)
- {
- sigset_t new_set, old_set = current->blocked;
- int error;
-
- if (set) {
- error = verify_area(VERIFY_READ, set, sizeof(sigset_t));
- if (error)
- return error;
- new_set = get_fs_long((unsigned long *) set) & _BLOCKABLE;
- switch (how) {
- case SIG_BLOCK:
- current->blocked |= new_set;
- break;
- case SIG_UNBLOCK:
- current->blocked &= ~new_set;
- break;
- case SIG_SETMASK:
- current->blocked = new_set;
- break;
- default:
- return -EINVAL;
- }
- }
- if (oset) {
- error = verify_area(VERIFY_WRITE, oset, sizeof(sigset_t));
- if (error)
- return error;
- put_fs_long(old_set, (unsigned long *) oset);
- }
- return 0;
- }
-
- asmlinkage int sys_sgetmask(void)
- {
- return current->blocked;
- }
-
- asmlinkage int sys_ssetmask(int newmask)
- {
- int old=current->blocked;
-
- current->blocked = newmask & _BLOCKABLE;
- return old;
- }
-
- asmlinkage int sys_sigpending(sigset_t *set)
- {
- int error;
- /* fill in "set" with signals pending but blocked. */
- error = verify_area(VERIFY_WRITE, set, 4);
- if (!error)
- put_fs_long(current->blocked & current->signal, (unsigned long *)set);
- return error;
- }
-
- /*
- * atomically swap in the new signal mask, and wait for a signal.
- */
- asmlinkage int sys_sigsuspend(int restart, unsigned long oldmask, unsigned long set)
- {
- unsigned long mask;
- struct frame * regs = (struct frame *) &restart;
-
- mask = current->blocked;
- current->blocked = set & _BLOCKABLE;
- regs->d0 = -EINTR;
- while (1) {
- current->state = TASK_INTERRUPTIBLE;
- schedule();
- if (do_signal(mask,regs))
- return -EINTR;
- }
- }
-
- /*
- * POSIX 3.3.1.3:
- * "Setting a signal action to SIG_IGN for a signal that is pending
- * shall cause the pending signal to be discarded, whether or not
- * it is blocked" (but SIGCHLD is unspecified: linux leaves it alone).
- *
- * "Setting a signal action to SIG_DFL for a signal that is pending
- * and whose default action is to ignore the signal (for example,
- * SIGCHLD), shall cause the pending signal to be discarded, whether
- * or not it is blocked"
- *
- * Note the silly behaviour of SIGCHLD: SIG_IGN means that the signal
- * isn't actually ignored, but does automatic child reaping, while
- * SIG_DFL is explicitly said by POSIX to force the signal to be ignored..
- */
- static void check_pending(int signum)
- {
- struct sigaction *p;
-
- p = signum - 1 + current->sigaction;
- if (p->sa_handler == SIG_IGN) {
- if (signum == SIGCHLD)
- return;
- current->signal &= ~_S(signum);
- return;
- }
- if (p->sa_handler == SIG_DFL) {
- if (signum != SIGCONT && signum != SIGCHLD && signum != SIGWINCH)
- return;
- current->signal &= ~_S(signum);
- return;
- }
- }
-
- asmlinkage int sys_signal(int signum, unsigned long handler)
- {
- struct sigaction tmp;
-
- if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
- return -EINVAL;
- if (handler >= TASK_SIZE)
- return -EFAULT;
- tmp.sa_handler = (void (*)(int)) handler;
- tmp.sa_mask = 0;
- tmp.sa_flags = SA_ONESHOT | SA_NOMASK;
- tmp.sa_restorer = NULL;
- handler = (long) current->sigaction[signum-1].sa_handler;
- current->sigaction[signum-1] = tmp;
- check_pending(signum);
- return handler;
- }
-
- asmlinkage int sys_sigaction(int signum, const struct sigaction * action,
- struct sigaction * oldaction)
- {
- struct sigaction new_sa, *p;
-
- if (signum<1 || signum>32 || signum==SIGKILL || signum==SIGSTOP)
- return -EINVAL;
- p = signum - 1 + current->sigaction;
- if (action) {
- memcpy_fromfs(&new_sa, action, sizeof(struct sigaction));
- if (new_sa.sa_flags & SA_NOMASK)
- new_sa.sa_mask = 0;
- else {
- new_sa.sa_mask |= _S(signum);
- new_sa.sa_mask &= _BLOCKABLE;
- }
- if (TASK_SIZE <= (unsigned long) new_sa.sa_handler)
- return -EFAULT;
- }
- if (oldaction) {
- if (!verify_area(VERIFY_WRITE,oldaction, sizeof(struct sigaction)))
- memcpy_tofs(oldaction, p, sizeof(struct sigaction));
- }
- if (action) {
- *p = new_sa;
- check_pending(signum);
- }
- return 0;
- }
-